﻿#include "ssOpenGLDevice.h"


namespace StarSeeker
{

#ifdef SS_USE_X11
	static int snglBuf[] = {GLX_RGBA, GLX_DEPTH_SIZE, 16, None};
	static int dblBuf[]  = {GLX_RGBA, GLX_DEPTH_SIZE, 16, GLX_DOUBLEBUFFER, None};
#endif

#if defined(_WIN32) && !defined(SS_USE_GLES2)
ssGLDeviceWin32::ssGLDeviceWin32()
{
	m_hWnd = 0;
	m_hDC = 0;
	m_hGLContext = 0;
}
bool ssGLDeviceWin32::InitGLDevice(HDISPLAY hdis, HWND hwnd)
{
	m_hWnd = hwnd;
	m_hDC = GetDC(m_hWnd);
	return m_hDC!=0;
}
void ssGLDeviceWin32::CloseDevice()
{
	if(wglGetCurrentContext()!=NULL)
	{
		wglMakeCurrent(NULL,NULL);
	}
	if(m_hGLContext)
	{
		wglDeleteContext(m_hGLContext);
		m_hGLContext = 0;
	}
}
bool ssGLDeviceWin32::ChooseConfig()
{
	//创建windows绘画设备
	PIXELFORMATDESCRIPTOR pfd;
	memset(&pfd,0,sizeof(PIXELFORMATDESCRIPTOR));
	pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
	pfd.nVersion = 1;
	pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_STEREO_DONTCARE | PFD_SWAP_LAYER_BUFFERS;
	pfd.iPixelType = PFD_TYPE_RGBA;
	pfd.cColorBits = 32;
	pfd.cDepthBits = 16;
	pfd.iLayerType = PFD_MAIN_PLANE;
	//HDC hdc = ::GetDC(m_hWnd);
	int pixelFormat = ChoosePixelFormat(m_hDC, &pfd);
	return SetPixelFormat(m_hDC, pixelFormat, &pfd)==TRUE;
}
bool ssGLDeviceWin32::CreateDevice()
{
	m_hGLContext = wglCreateContext(m_hDC);
	return m_hGLContext!=0;
}
bool ssGLDeviceWin32::SetCurrent()
{
	wglMakeCurrent(m_hDC, m_hGLContext);
	return wglGetCurrentContext()!=0;
}
#endif

#ifdef SS_USE_X11
ssGLDeviceX11::ssGLDeviceX11()
{
	m_dpy = 0;
	m_hWnd = 0;
	m_vi = 0;
	m_glContext = 0;
	m_doubleBuffer = true;
}
bool ssGLDeviceX11::InitGLDevice(HDISPLAY hdis, HWND hwnd)
{
    m_dpy = hdis;
    m_hWnd = hwnd;
    int dummy;
    if (!m_dpy)
    {
    	return false;
    }
    //glx函数是否支持
    if (!glXQueryExtension(m_dpy, &dummy, &dummy))
    {
        return false;
    }
	return true;
}
void ssGLDeviceX11::CloseDevice()
{
}
bool ssGLDeviceX11::ChooseConfig()
{
	//创建画面格式
	m_vi = glXChooseVisual(m_dpy, DefaultScreen(m_dpy), dblBuf);
	if (!m_vi)
	{
		m_vi = glXChooseVisual(m_dpy, DefaultScreen(m_dpy), snglBuf);
		if (!m_vi) return false;
		m_doubleBuffer = false;
	}
	//    if(m_vi->class != TrueColor)
	//    {
	//        return false;
	//    }
	return true;
}
bool ssGLDeviceX11::CreateDevice()
{
	//显示设备上下文
	m_glContext = glXCreateContext(m_dpy, m_vi, /* no shared dlists */ None,
		/* direct rendering if possible */ GL_TRUE);
	if (!m_glContext)
	{
		return false;
	}
	return true;
}
bool ssGLDeviceX11::SetCurrent()
{
	if (glXMakeCurrent(m_dpy, m_hWnd, m_glContext))
	{
		XMapWindow(m_dpy, m_hWnd);
		return true;
	}
	return false;
}
#endif

#if defined(_WIN32) && defined(SS_USE_GLES2)
struct EglStruct
{
	EGLDisplay  dsp;
	EGLConfig   cfg;
	EGLContext  cxt;
	EGLSurface  surf;
} g_egl;
ssGLES2Device::ssGLES2Device()
{
	m_eglDisplay = 0;
	m_hWnd = 0;
	m_eglSurface = 0;
	m_eglContext = 0;
}
bool ssGLES2Device::InitGLDevice(HDISPLAY hdis, HWND hwnd)
{
	EGLint majorVersion;
	EGLint minorVersion;
	m_hWnd = hwnd;
	// Get Display
	m_eglDisplay = eglGetDisplay( EGL_DEFAULT_DISPLAY );
	if ( m_eglDisplay == EGL_NO_DISPLAY )
	{
		return false;
	}
	// Initialize EGL
	if ( !eglInitialize( m_eglDisplay, &majorVersion, &minorVersion) )
	{
		return false;
	}
	return true;
}
void ssGLES2Device::CloseDevice()
{
	eglDestroyContext( m_eglDisplay,m_eglContext );
	eglDestroySurface( m_eglDisplay, m_eglSurface );
	eglTerminate( m_eglDisplay );
}
bool ssGLES2Device::ChooseConfig()
{
	EGLint numConfigs;
	EGLint attribs[] = {EGL_BUFFER_SIZE,    EGL_DONT_CARE,
						EGL_DEPTH_SIZE,     16,
						EGL_ALPHA_SIZE,		0,
						EGL_RED_SIZE,       5,
						EGL_GREEN_SIZE,     6,
						EGL_BLUE_SIZE,      5,
						//EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
						EGL_STENCIL_SIZE,	0,
						EGL_SAMPLES,		4,
						EGL_NONE};
	// Get configs
	if ( !eglGetConfigs( m_eglDisplay, NULL, 0, &numConfigs ) )
	{
		return false;
	}
	// Choose config
	if ( !eglChooseConfig(m_eglDisplay, attribs, m_configs, 1, &numConfigs) )
	{
		return false;
	}
	return true;
}
bool ssGLES2Device::CreateDevice()
{
	// Create a surface
	m_eglSurface = eglCreateWindowSurface( m_eglDisplay, m_configs[0], m_hWnd, NULL );
	if ( m_eglSurface == EGL_NO_SURFACE )
	{
		return false;
	}
	// Set the current rendering API (EGL_OPENGL_API, EGL_OPENGL_ES_API,EGL_OPENVG_API)
	EGLBoolean eRetStatus = eglBindAPI(EGL_OPENGL_ES_API);
	if (eRetStatus != EGL_TRUE)
	{
		return false;
	}
	// Create a GL context
	EGLint ctxAttribList[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
	m_eglContext = eglCreateContext( m_eglDisplay, m_configs[0], EGL_NO_CONTEXT, ctxAttribList );
	if ( m_eglContext == EGL_NO_CONTEXT )
	{
		return false;
	}
	//If interval is set to a value of 0, buffer swaps are not synchronized to a video frame, and the swap happens as soon as the render is complete.
	eglSwapInterval(m_eglDisplay,0);
	return true;
}
bool ssGLES2Device::SetCurrent()
{
	EGLBoolean eRetStatus = eglMakeCurrent (m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext);
	if( eRetStatus != EGL_TRUE )
	{
		return false;
	}
	return true;
}
#endif


ssGLDevice* CreateGLDevice()
{
#ifdef _WIN32
#ifdef SS_USE_GLES2
	return new ssGLES2Device;
#else
	return new ssGLDeviceWin32;
#endif
#endif
#ifdef SS_USE_X11
	return new ssGLDeviceX11;
#endif
#ifdef SS_USE_ANDROID
	return 0;
#endif
	return 0;
}

}
